package simulation;
import java.nio.FloatBuffer;

import org.lwjgl.input.*;
import org.lwjgl.opengl.Display;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.*;
import org.lwjgl.util.Timer;

public class Simulation {

	private static Timer fpsTimer = new Timer();
	private static float lastTimerTick;
	
	private static String title = "VL Computergraphik";
	private static int width = 800, height = 600, depth = 10;
	
	public static Blitz bli[]=new Blitz[2];
	
	public void launch(String[] argv) {
		try {
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
			Display.setTitle(title);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GLU.gluPerspective(50.0f, 1.0f*width/height, 0.1f, 1f*depth);	// glOrtho, glFrustum, gluPerspective/gluLookAt
		GLU.gluLookAt(0, 0, 0, 0, 0, 1, 0, 1, 0);

		GL11.glEnable(GL11.GL_BLEND);
		//GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
		GL11.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
		
		initializeLighting();
		
		// and initialize Lightning!
		bli[0]=new Blitz(2048); bli[0].lightid=1; //bli[0].enableSpotlight=true;
		bli[1]=new Blitz(2048); bli[0].lightid=2; //bli[1].enableSpotlight=true;
		double startpunkt[]={-1.5,0,5};
		double richtung[]={1,0,-1};
		double spleisswahrsch=0.55;
		int laenge=20;
		double dicke=0.035;
		double geolaenge=0.4;
		float bias[]={0,0,0};
		bli[0].preset(startpunkt, richtung, spleisswahrsch, laenge, dicke, geolaenge, bias);
		bli[0].regenerate();
		
		richtung=bli[1].createVertex(1.1,0.2,-0.8);
		spleisswahrsch=0.55;
		laenge=15;
		dicke=0.035;
		geolaenge=0.4;
		bli[1].preset(startpunkt, richtung, spleisswahrsch, laenge, dicke, geolaenge, bias);
		bli[1].regenerate();
				
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		
		// ------------------------------------------------------
		
		lastTimerTick = fpsTimer.getTime();
		double lastFpsUpdate = lastTimerTick;
		int fps = 0;
		
		while (!Display.isCloseRequested()) {
			Timer.tick();
			// delta = fpsTimer.getTime() - lastTimerTick;
			lastTimerTick = fpsTimer.getTime();
			
			if (fpsTimer.getTime() - lastFpsUpdate > 1) {
				Display.setTitle(String.format(title + " (%d fps)", fps));
				lastFpsUpdate++;
				fps = 0;
			}
			fps++;
			if (Mouse.isButtonDown(0))
			{
				int ix=Mouse.getX()-600;
				int iy=Mouse.getY()-300;
				double dx=(double)ix/400d; double dy=(double)iy/300d;
				richtung[0]=-dx;richtung[1]=dy;
				bli[1].preset(startpunkt, richtung, spleisswahrsch, laenge, dicke, geolaenge, bias);
				
			}
			
			renderGL();
			Display.update();
			Display.sync(60); // cap fps to 60fps
		}
		Display.destroy();
	}
	
	public static void renderGL() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		
		// draw colorful circle
		GL11.glPushMatrix();
			GL11.glTranslated(1.5, 0, depth/2);
			
			GL11.glBegin(GL11.GL_TRIANGLE_FAN);
				circle(1, 500);
			GL11.glEnd();
		GL11.glPopMatrix();
		
		// draw sphere
		GL11.glPushMatrix();
			GL11.glTranslated(-1.5, 0, depth/2);
			
			GL11.glBegin(GL11.GL_QUADS);
				GL11.glColor3d(0,0,1);
				sphere(3, 3);
			GL11.glEnd();
		GL11.glPopMatrix();
		if(Math.random()<0.1) bli[0].regenerate();
		if(Math.random()<0.9) bli[1].regenerate();
		bli[0].draw(1,false); bli[1].draw(1,false);
	}
	
	private static void initializeLighting() {
		// let there be light!
		GL11.glEnable(GL11.GL_LIGHTING);
		GL11.glEnable(GL11.GL_LIGHT0);
		
		GL11.glEnable(GL11.GL_COLOR_MATERIAL);
		GL11.glColorMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE);
		
		// materials
		FloatBuffer matSpecular = BufferUtils.createFloatBuffer(4);
		matSpecular.put(1.0f).put(1.0f).put(1.0f).put(1.0f).flip();
		
		GL11.glShadeModel(GL11.GL_SMOOTH);
		GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, matSpecular);
		GL11.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 50.0f);
		
		// lights
		FloatBuffer lightPos = BufferUtils.createFloatBuffer(4);
		lightPos.put(0.0f).put(0.0f).put(7.0f).put(1.0f).flip();	// 0, 0, 7, 1; 1, 1, 1, 0
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, lightPos);
		/*
		
		FloatBuffer whiteLight = BufferUtils.createFloatBuffer(4);
		whiteLight.put(1.0f).put(1.0f).put(1.0f).put(1.0f).flip();
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, whiteLight);	// sets specular light to white
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, whiteLight);	// sets diffuse light to white
		
		
		FloatBuffer lModelAmbient = BufferUtils.createFloatBuffer(4);
		lModelAmbient.put(0.5f).put(0.5f).put(0.5f).put(1.0f).flip();
		GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, lModelAmbient);
		*/
		
	}
	
	private static double degToRad = 2*Math.PI/180;
	public static void sphere(double dtheta, double dphi) {
		for (double theta = -90; theta <= 90 - dtheta; theta += dtheta) {
			for (double phi = 0; phi <= 360 - dphi; phi += dphi) {
				GL11.glVertex3d(Math.cos(theta*degToRad) * Math.cos(phi*degToRad),
						Math.cos(theta*degToRad) * Math.sin(phi*degToRad),
						Math.sin(theta*degToRad));

				GL11.glVertex3d(Math.cos((theta+dtheta)*degToRad) * Math.cos(phi*degToRad),
						Math.cos((theta+dtheta)*degToRad) * Math.sin(phi*degToRad),
						Math.sin((theta+dtheta)*degToRad));

				GL11.glVertex3d(Math.cos((theta+dtheta)*degToRad) * Math.cos((phi+dphi)*degToRad),
						Math.cos((theta+dtheta)*degToRad) * Math.sin((phi+dphi)*degToRad),
						Math.sin((theta+dtheta)*degToRad));
     
				if (theta > -90 && theta < 90) {
					GL11.glVertex3d(Math.cos(theta*degToRad) * Math.cos((phi+dphi)*degToRad),
							Math.cos(theta*degToRad) * Math.sin((phi+dphi)*degToRad),
							Math.sin(theta*degToRad));
				}
			}
		}
	}
	
	public static void circle(double radius, double stepCount) {
		GL11.glColor3d(1,1,1);
		GL11.glVertex3d(0,0,0);	// origin
		
		GL11.glColor3d(0,1,0);
		
		double angle;
		for (int step = 0; step < stepCount; step++) {
			angle = 2.0*Math.PI * step/stepCount;
			GL11.glColor3d(Math.cos(angle), Math.sin(angle), 1);
			GL11.glVertex3d(radius * Math.cos(angle), radius * Math.sin(angle), 0);
		}
		GL11.glVertex3d(radius, 0, 0);	// close the circle
	}
}
